const log4js = require('log4js');           // 导入日志插件
log4js.configure('log4js.json');            // 导入配置文件
const logger = log4js.getLogger('console');    // 使用file类型的日志实例

logger.info('Please use UTF-8 encoding!');
logger.info('CMD: chcp 65001');
logger.info('Loading library files......');

const fs = require('fs');
const vm = require('vm');
const path = require('path')
const path_join = require('path').join
const http = require('http');
const https = require('https');
const url = require('url');
const util = require('util');
const events = require("events");
const websocket = require("nodejs-websocket");
const { createClient } = require("oicq")
const { Worker, isMainThread } = require('worker_threads');
const { MessageChannel } = require('worker_threads');
const WebSocketClient = require('websocket').client;
const { v4 } = require('uuid');
const uuidv4 = v4;
const runpath = __dirname;

const sys_version = require(runpath + '/version.js');
const {getLogger} = require("log4js");

logger.info('切换控制台日志语言：【中文】！');
logger.info('为了防止乱码，请使用UTF-8编码！');
logger.info('进入全局【TRY/CATCH】异常捕获模式！');

function tryMakedir(path) {
    fs.mkdirSync(path, { recursive: true });
}

tryMakedir(runpath + "/data");
tryMakedir(runpath + "/libs");
tryMakedir(runpath + "/config");
tryMakedir(runpath + "/config/plugins");
tryMakedir(runpath + "/logs");
tryMakedir(runpath + "/plugins");
tryMakedir(runpath + "/runtime");

function catchErrorPrint(e) {
    logger.error('程序（异步）发生异常：', e);
}

const emitter = new events.EventEmitter();
emitter.addListener("error", function (e) {
    logger.error('程序发生异常【EVENT捕获】：', e);
});

function ListFilesByPath(jsonPath) {
    let theFiles = [];
    function findTheFile(path) {
        let files = fs.readdirSync(path);
        files.forEach(function (item, index) {
            let fPath = path_join(path, item);
            let stat = fs.statSync(fPath);
            if (stat.isDirectory() === true) {
                findTheFile(fPath);
            }
            if (stat.isFile() === true) {
                theFiles.push(fPath);
            }
        });
    }
    findTheFile(jsonPath);
    //console.log(theFiles);
    return theFiles;
}

function getIndex(arr,item){
    for (let i in arr) {
        if (arr[i] === item) {
            return i;
        }
    }
}

process.on('unhandledRejection', (reason, p) => {
    logger.error('程序（异步）发生未被捕获的异常：\"', p, "\"", "，原因：", "\"", reason, "\"");
});

logger.info('当前脚本所在目录：', runpath);

/*
* 快速生成JSON消息
*/
function genWebsocketReturn(type, message) {
    return { "type": type, "message": message ,"time" : Date.now(),"uuid": uuidv4()}
}



function genWebsocketReturn_Event(event_name, event_obj) {
    return genWebsocketReturn("event",{ "name": event_name, "data": event_obj })

}



try {
    logger.info("欢迎使用 " + sys_version.ProgramName + " ！");
    logger.info("程序版本：" + sys_version.Version + " 【" + sys_version.VersionType + "】");

    logger.info('正在加载配置文件......');

    // 读取JSON配置文件
    let qqcfg_data = fs.readFileSync('config/userdata.json').toString();
    let qqcfg_array = JSON.parse(qqcfg_data);
    let syscfg_data = fs.readFileSync('config/system.json').toString();
    let syscfg_array = JSON.parse(syscfg_data);
    let wscfg_data = fs.readFileSync('config/wsclient.json').toString();
    let wscfg_array = JSON.parse(wscfg_data);

    //设置QQ账号
    const qq_account = qqcfg_array.account;
    //创建QQ客户端
    const qq_client = createClient(qq_account)


    const pluginApi = require(runpath + '/libs/pluginApi');
    const pluginTools = require(runpath + '/libs/pluginTools');

    //const webApi = require(runpath + "/webApi.js");



    qq_client.on("system.online", () => function () {
        logger.info('账号登录成功！');
    })

    qq_client.on("system.login.qrcode", function (e) {
        logger.info('请在扫码后按下回车登录！');
        //扫码后按回车登录
        process.stdin.once("data", () => {
            this.login().then(function () { }).catch((e) => { catchErrorPrint(e); });
        })
    })

    /*qq_client.on("message", e => {
        console.log(e)
        e.reply("hello world", true) //true表示引用对方的消息
    })*/

    //logger.info('准备加载插件【OnLoad_BeforeStarting】......');
    // 列出所有插件
    let pluginFileList = ListFilesByPath(runpath + '/plugins');
    for (let i = 0; i < pluginFileList.length; i++) {
        //if(path.extname(pluginFileList[i]) != ".js"){
        if (!pluginFileList[i].endsWith(".js")) {
            //pluginFileList.slice(i)
            //pluginFileList = pluginFileList.slice(i, i)
            pluginFileList.splice(i, 1);

        }

    }
    logger.info('已检测到插件：\n', pluginFileList);
    logger.info('正在构造插件列表......');
    let pluginList = [];
    if (syscfg_array.DebugMode === true) {
        logger.debug('变量【pluginFileList】的值：', pluginFileList);
    }
    for (let i = 0; i < pluginFileList.length; i++) {
        if (syscfg_array.DebugMode === true) {
            logger.debug('变量【pluginFileList[i]】的值（在For循环中）：', pluginFileList[i]);
        }

        let obj;
        obj = require(pluginFileList[i]);
        pluginList.push([pluginFileList[i], obj])
    }


    for (let i = 0; i < pluginList.length; i++) {
        if (pluginList[i][1].PluginConfig === undefined) {
            /*if(pluginListKey.IsLib != true){}*/
            logger.warn('跳过了没有描述信息的插件：\"', pluginList[i][0], "\"");
            pluginList.splice(i, 1);
        }

    }

    // 判断配置文件里是否开启 WS 功能

    if (wscfg_array.useWS === true) {
        let ws_cfg_serverList = wscfg_array.Servers;
        let ws_serverList = [];
        let ws_connList = [];

        const GlobalVMContext = {
            "qq_client": qq_client,
            "qq_account": qq_account,
            "pluginApi":pluginApi,
            "pluginTools": pluginTools,
            "runPath": runpath,
            "logger":getLogger("WebApiServer"),
            "uuidv4": uuidv4,
            "url": url,
            "util": util,
            "http": http,
            "https": https,
            "__global_session__":{
                "uuid": uuidv4(),
                "time": Date.now()
            }

        }


        function HandleWebSocketMSG(wslink,message) {
            let script;
            try {
                if (syscfg_array.Function.Print_WS_Messages === true) {
                    if (syscfg_array.Function.Print_WS_Messages_Direct === true) {
                        console.log(message)
                    } else {
                        logger.info("【WebApi信息】服务端链接：", wslink, "\n", message)
                    }
                }
                let msg_json = JSON.parse(message.utf8Data);
                if (msg_json.type === "code") {
                    let LocalVMContext = {
                        "__session": {
                            "uuid": uuidv4(),
                            "time": Date.now()
                        },
                        "api": GlobalVMContext,
                        "wslink": wslink,
                        // 还是加上这些东西吧qwq
                        // 顺便做点简化啥的
                        "qq": qq_client,
                        "qq_client": qq_client,
                        "qqNumber": qq_account,
                        "qq_account": qq_account,

                        "pApi": pluginApi,
                        "pluginApi": pluginApi,

                        "pTools": pluginTools,
                        "pluginTools": pluginTools,

                        "runPath": runpath,
                        "logger": getLogger("WebApiServer"),
                        "uuidv4": uuidv4,
                        "url": url,
                        "util": util,
                        "http": http,
                        "https": https,
                    }
                    let code = Buffer.from(msg_json.message.data, 'base64').toString('utf-8');
                    script = new vm.Script(code);
                    vm.createContext(LocalVMContext)
                    script.runInContext(LocalVMContext);
                }


            } catch (e) {
                logger.error("【WebApi信息】处理客户端发来的信息时发送异常，服务端链接：", wslink, "\n【收到的信息】", message, "\n【错误信息】", e)
            }
        }







        logger.info("当前WebSocket服务器列表：",ws_serverList)
        logger.info('正在创建WebSocket客户端......');
        // 链接 WS 服务器，并将其置于数组 ws_serverList 中
        for (let i = 0; i < ws_cfg_serverList.length; i++) {
            try {
                logger.info('【WebSocket】创建客户端：', ws_cfg_serverList[i]);
                //let ws = new WebSocket(ws_cfg_serverList[i]);
                /*ws.onopen = function(e){
                    let link = ws_cfg_serverList[i];
                    logger.info('【WebSocket】客户端事件：连接服务器成功！\n',"【WebSocket链接】",link);
                }
                ws.onclose = function(e){
                    let link = ws_cfg_serverList[i];
                    logger.info('【WebSocket】客户端事件：服务器关闭！\n',"【WebSocket链接】",link,
                        "\n【代码】",e.code,"\n【原因】",e.reason);
                }
                ws.onerror = function(){
                    let link = ws_cfg_serverList[i];
                    logger.info('【WebSocket】客户端事件：连接出现异常！\n',"【WebSocket链接】",link);
                }
                // 接收服务器的消息
                ws.onmessage = function(e){
                    let link = ws_cfg_serverList[i];
                    HandleWebSocketMSG(link,e)
                }*/
                let client = new WebSocketClient();
                client.on('connectFailed', function (error) {
                    let link = ws_cfg_serverList[i];
                    logger.error('【WebSocket】客户端事件：连接失败！\n', "【WebSocket链接】", link, "\n【错误信息】", error);
                });

                client.on('connect', function (connection) {
                    let link = ws_cfg_serverList[i];
                    logger.info('【WebSocket】客户端事件：连接服务器成功！\n', "【WebSocket链接】", link);
                    ws_connList.push([link, connection])

                    connection.on('error', function (error) {
                        let link = ws_cfg_serverList[i];
                        logger.error('【WebSocket】客户端事件：连接出现异常！\n', "【WebSocket链接】", link, "\n【错误信息】", error);
                    });
                    connection.on('close', function () {
                        let link = ws_cfg_serverList[i];
                        logger.warn('【WebSocket】客户端事件：连接关闭！\n', "【WebSocket链接】", link);
                    });
                    connection.on('message', function (message) {
                        /*if (message.type === 'utf8') {
                            console.log("Received: '" + message.utf8Data + "'");
                        }*/
                        let link = ws_cfg_serverList[i];
                        HandleWebSocketMSG(link, message)

                    });

                });

                //下面不能加'echo-protocol'，否则会报Can`t connect due to "Sec-WebSocket-Protocol header"的错。因为服务器没有返回对应协议规定的信息
                client.connect(ws_cfg_serverList[i]); //, 'echo-protocol');

                // 放进数组
                ws_serverList.push([ws_cfg_serverList[i], client]);
            }catch (e){
                logger.error('创建WebSocket客户端时出现异常：',ws_cfg_serverList[i],"\n","【异常信息】",e);
            }
        }
        logger.info('WebSocket客户端创建完成！');


        // 此方法用于向所有 WS 链接"广播"消息
        function sendWebSocketMSG(data) {
            for (let i = 0; i < ws_connList.length; i++) {
                let jsonStr = JSON.stringify(data);
                ws_connList[i][1].send(jsonStr)
            }
        }



        logger.info('正在注册WebSocket消息事件......');
        // 将所有官网罗列的消息事件转发到另一个线程

        qq_client.on("system.login.qrcode", e => {
            let data2;
            data2 = genWebsocketReturn_Event("system.login.qrcode", e)
            sendWebSocketMSG(data2)

        })
        qq_client.on("system.login.slider", e => {
            let data2;
            data2 = genWebsocketReturn_Event("system.login.slider", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("system.login.device", e => {
            let data2;
            data2 = genWebsocketReturn_Event("system.login.device", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("system.login.error", e => {
            let data2;
            data2 = genWebsocketReturn_Event("system.login.error", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("system.online", e => {
            let data2;
            data2 = genWebsocketReturn_Event("system.online", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("system.offline.kickoff", e => {
            let data2;
            data2 = genWebsocketReturn_Event("system.offline.kickoff", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("system.offline.network", e => {
            let data2;
            data2 = genWebsocketReturn_Event("system.offline.network", e)
            sendWebSocketMSG(data2)
        })

        qq_client.on("request.friend", e => {
            let data2;
            data2 = genWebsocketReturn_Event("request.friend", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("request.group.add", e => {
            let data2;
            data2 = genWebsocketReturn_Event("request.group.add", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("request.group.invite", e => {
            let data2;
            data2 = genWebsocketReturn_Event("request.group.invite", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("request", e => {
            let data2;
            data2 = genWebsocketReturn_Event("request", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("message.group", e => {
            let data2;
            data2 = genWebsocketReturn_Event("message.group", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("message.private", e => {
            let data2;
            data2 = genWebsocketReturn_Event("message.private", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("message.discuss", e => {
            let data2;
            data2 = genWebsocketReturn_Event("message.discuss", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("message", e => {
            let data2;
            data2 = genWebsocketReturn_Event("", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.friend.increase", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.friend.increase", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.friend.decrease", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.friend.decrease", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.friend.recall", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.friend.recall", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.friend.poke", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.friend.poke", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.friend", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.friend", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.group.increase", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.group.increase", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.group.decrease", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.group.decrease", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.group.recall", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.group.recall", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.group.poke", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.group.poke", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.group.ban", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.group.ban", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.group.admin", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.group.admin", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.group.transfer", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.group.transfer", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice.group", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice.group", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("notice", e => {
            let data2;
            data2 = genWebsocketReturn_Event("notice", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("sync.message", e => {
            let data2;
            data2 = genWebsocketReturn_Event("sync.message", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("sync.read", e => {
            let data2;
            data2 = genWebsocketReturn_Event("sync.read", e)
            sendWebSocketMSG(data2)
        })
        qq_client.on("guild.message", e => {
            let data2;
            data2 = genWebsocketReturn_Event("guild.message", e)
            sendWebSocketMSG(data2)
        })

        /*
        qq_client.on("", e => {
            post2.postMessage({ "type": "event", "event": "", "data": e });
        })
        */


    }



    logger.info('正在初始化插件......');
    for (let i = 0; i < pluginList.length; i++) {
        try {
            logger.info('正在初始化：', pluginList[i][1].PluginConfig.Name, "\n",
                "【插件ID】", pluginList[i][1].PluginConfig.ID, "\n",
                "【插件版本】", pluginList[i][1].PluginConfig.Version, "\n",
                "【插件作者】", pluginList[i][1].PluginConfig.Anthor, "\n",
                "【插件路径】", pluginList[i][0], "\n",
            );


            pluginList[i][1].Initialize(qq_client, pluginApi, pluginTools, runpath, runpath + "/config/plugins", pluginList[i][0]);

        } catch (e) {
            logger.warn("初始化插件时遇到错误，插件路径：", pluginList[i][0], "\n", "【错误信息】", e)


        }
    }
    logger.info('插件初始化完成！');









    qq_client.login().then(function () { }).catch((e) => { catchErrorPrint(e); });
    let qq_isOnline = qq_client.isOnline();
    logger.info("客户端在线状态（存在延时）：", qq_isOnline);

    logger.info('正在加载插件......');
    for (let i = 0; i < pluginList.length; i++) {
        try {
            logger.info('正在加载：', pluginList[i][1].PluginConfig.Name, "\n",
                "【插件ID】", pluginList[i][1].PluginConfig.ID, "\n",
                "【插件版本】", pluginList[i][1].PluginConfig.Version, "\n",
                "【插件作者】", pluginList[i][1].PluginConfig.Anthor, "\n",
                "【插件路径】", pluginList[i][0], "\n",
            );


            pluginList[i][1].OnLoad(qq_client, pluginApi, pluginTools, runpath, runpath + "/config/plugins", pluginList[i][0]);

        } catch (e) {
            logger.warn("加载插件时遇到错误，插件路径：", pluginList[i][0], "\n", "【错误信息】", e)


        }
    }
    logger.info('插件加载完成！');




    logger.info('正在注册主消息事件......');
    qq_client.on("message", e => {
        if (syscfg_array.Function.Print_Messages_When_Received === true) {
            if (syscfg_array.Function.Print_Messages_When_Received_Direct === true) {
                console.log(e)
            } else {
                logger.info("【信息】", "\n", e)
            }

        }
        //e.reply("hello world", true) //true表示引用对方的消息
        for (let i = 0; i < pluginList.length; i++) {
            try {
                /*logger.info('正在加载：',pluginList[i][1].PluginConfig.Name,"\n",
                    "【插件ID】",pluginList[i][1].PluginConfig.ID,"\n",
                    "【插件版本】",pluginList[i][1].PluginConfig.Version,"\n",
                    "【插件作者】",pluginList[i][1].PluginConfig.Anthor,"\n",
                    "【插件路径】",pluginList[i][0],"\n",
                );*/
                pluginList[i][1].OnMessage(qq_client, pluginApi, pluginTools, runpath, runpath + "/config/plugins", pluginList[i][0], e);
            } catch (e) {
                if (syscfg_array.DebugMode === true) {
                    logger.debug("调用插件消息事件时遇到错误，插件路径：", pluginList[i][0], "\n", "【错误信息】", e)
                }


            }
        }

    })
    logger.info('注册主消息事件完成！');



    logger.info('所有任务已完成！');














} catch (e) {
    logger.error('程序（同步）发生异常：', e);
    logger.warn('程序正在退出......')
    process.exit(1);
}

